﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
using JCPogi.Core.Helpers;

namespace JCPogi.Core {

    public class SmsBlaster : IDisposable {

        public SmsBlaster(JCPogiModemConfiguration configuration) {
            _configuration = configuration;            
        }

        /// <summary>
        /// Sets the recipient list of the SMS blast
        /// </summary>
        /// <param name="recipientList">
        /// a string containing one to many recipients, separated by a comma, a semi-colon or a new-line character
        /// </param>
        /// <returns>
        /// The current instance
        /// </returns>
        public SmsBlaster ToRecipients(string recipientList) {
            _recipients = recipientList;
            return this;
        }

        /// <summary>
        /// Sets the message template to be used for the text blast
        /// </summary>
        /// <param name="message">The message to be used for the text blast</param>
        /// <returns>The current instance</returns>
        public SmsBlaster WithMessage(string message) {
            _message = message;
            return this;
        }

        public virtual IEnumerable<ValidationResult> Prepare() {            
            var errors = new List<ValidationResult>();
            
            PrepareRecipients(errors);
            PrepareMessage(errors);
            PrepareDevice(errors);

            return errors;
        }

        public DeviceCommandContext CreateContext() {
            string errorMessage = "The blaster has not been prepared. Make sure Prepare() is invoked before CreateContext().";
            
            Protect.AgainstInvalidOperation(_recipientList.IsEmpty(), errorMessage);
            Protect.AgainstInvalidOperation(_renderedMessage.IsNullOrEmpty(), errorMessage);
            Protect.AgainstInvalidOperation(_communicationDevice.IsNull(), errorMessage);

            var context = new SendBulkSmsCommandContext(_recipientList, _message, _communicationDevice);                
            return context;
        }

        private void PrepareRecipients(List<ValidationResult> errors) {
            string errorMessage = "At least one (1) recipient should be in the list.";

            _recipients = _recipients ?? string.Empty;
            _recipients = _recipients
                .Trim()
                .Replace(" ", string.Empty)
                .Replace("\r", string.Empty);

            if (string.IsNullOrEmpty(_recipients)) {
                errors.Add(new ValidationResult(errorMessage));
                return;
            }

            // delimiters: comma, semi-colon, \n
            string[] recipientEntries = _recipients.Split(new char[] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string entry in recipientEntries) {
                string phoneNumber = entry.Replace(" ", string.Empty);
                
                //TODO: enhance validation to international phone number format using regex
                if (!string.IsNullOrEmpty(phoneNumber) && !_recipientList.Contains(phoneNumber)) {
                    _recipientList.Add(phoneNumber);
                }
            }

            if (!_recipientList.Any()) {
                errors.Add(new ValidationResult(errorMessage));                
            }
        }

        private void PrepareMessage(List<ValidationResult> errors) {
            string errorMessage = "The message must be at least two (2) characters and not more than 160 characters.";
            _message = _message ?? string.Empty;

            _message = _message.Trim();

            if (!(_message.Length >= 2 && _message.Length <= 160)) {
                errors.Add(new ValidationResult(errorMessage));
                return;
            }

            _renderedMessage = _message;
        }

        private void PrepareDevice(List<ValidationResult> errors) {
            try {
                _communicationDevice = CommunicationDevice.Open(_configuration.COMPort);
            }
            catch {
                string errorMessage = "Unable to open device on {0}".WithTokens(_configuration.COMPort);
                errors.Add(new ValidationResult(errorMessage));
            }
        }

        #region IDisposable

        public void Dispose() {
            if (_communicationDevice != null) {
                _communicationDevice.Dispose();
                _communicationDevice = null;
            }
        }

        #endregion

        private readonly JCPogiModemConfiguration _configuration;        
        private readonly List<string> _recipientList = new List<string>();
        private string _recipients;
        private string _message;
        private string _renderedMessage;
        private CommunicationDevice _communicationDevice;

    }
}
